{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "e9a3b3c2",
   "metadata": {},
   "outputs": [],
   "source": [
    "from utils.preprocessing import DataLoader\n",
    "import gurobipy as gp\n",
    "from gurobipy import *"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "022b18ae",
   "metadata": {},
   "source": [
    "# 数据读取"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "ae1d3c1d",
   "metadata": {},
   "outputs": [],
   "source": [
    "data_loader = DataLoader('data/Data A-Crew.csv', 'data/Data A-Flight.csv')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "58b038b4",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Counting times...(0/206)\n",
      "Counting times...(100/206)\n",
      "Counting times...(200/206)\n",
      "## Loading C\n",
      "## C loaded\n",
      "## Loading AP\n",
      "## AP loaded\n",
      "## Loading Base\n",
      "## Base loaded\n",
      "## Loading F\n",
      "## F loaded\n",
      "## Loading FF\n",
      "Loading data... (10000/42436)\n",
      "Loading data... (20000/42436)\n",
      "Loading data... (30000/42436)\n",
      "Loading data... (40000/42436)\n",
      "## FF loaded\n",
      "## Loading FF1\n",
      "Loading data... (10000/42436)\n",
      "Loading data... (20000/42436)\n",
      "Loading data... (30000/42436)\n",
      "Loading data... (40000/42436)\n",
      "## FF1 loaded\n",
      "## Loading FF2\n",
      "Loading data... (10000/42436)\n",
      "Loading data... (20000/42436)\n",
      "Loading data... (30000/42436)\n",
      "Loading data... (40000/42436)\n",
      "## FF2 loaded\n",
      "Counting times...(0/206)\n",
      "Counting times...(100/206)\n",
      "Counting times...(200/206)\n"
     ]
    }
   ],
   "source": [
    "data_loader.dump_data()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "5e211db9",
   "metadata": {},
   "outputs": [],
   "source": [
    "data_loader_a1 = DataLoader('data/Data A-Crew.csv', 'data/Data A-Flight.csv')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "e4330201",
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Counting times...(0/108)\n",
      "Counting times...(100/108)\n",
      "## Loading C\n",
      "## C loaded\n",
      "## Loading AP\n",
      "## AP loaded\n",
      "## Loading Base\n",
      "## Base loaded\n",
      "## Loading F\n",
      "## F loaded\n",
      "## Loading FF\n",
      "Loading data... (10000/11664)\n",
      "## FF loaded\n",
      "## Loading FF1\n",
      "Loading data... (10000/11664)\n",
      "## FF1 loaded\n",
      "## Loading FF2\n",
      "Loading data... (10000/11664)\n",
      "## FF2 loaded\n",
      "Counting times...(0/108)\n",
      "Counting times...(100/108)\n"
     ]
    }
   ],
   "source": [
    "data_loader_a1.dump_data(cropped_date=(11,18))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "add17372",
   "metadata": {},
   "source": [
    "# 模型建立与求解"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "77e8c542",
   "metadata": {},
   "source": [
    "## 第一阶段"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9287ac58",
   "metadata": {},
   "source": [
    "### 优化目标①"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "79ec895a",
   "metadata": {},
   "outputs": [],
   "source": [
    "m=gp.Model('m1')\n",
    "\n",
    "z=m.addVars(data_loader_a1.F,vtype=gp.GRB.BINARY,name='z')\n",
    "x_ikdh=m.addVars(data_loader_a1.F,data_loader_a1.C,vtype=gp.GRB.BINARY,name='x_dh')\n",
    "x_ikfo=m.addVars(data_loader_a1.F,data_loader_a1.C,vtype=gp.GRB.BINARY,name='x_fo')\n",
    "x_ikcap=m.addVars(data_loader_a1.F,data_loader_a1.C,vtype=gp.GRB.BINARY,name='x_cap')\n",
    "\n",
    "r_iksta=m.addVars(data_loader_a1.F,data_loader_a1.C,vtype=gp.GRB.BINARY,name='r_iksta')\n",
    "r_jkfin=m.addVars(data_loader_a1.F,data_loader_a1.C,vtype=gp.GRB.BINARY,name='r_jkfin')\n",
    "d_iksta=m.addVars(data_loader_a1.F,data_loader_a1.C,vtype=gp.GRB.BINARY,name='d_iksta')\n",
    "d_jkfin=m.addVars(data_loader_a1.F,data_loader_a1.C,vtype=gp.GRB.BINARY,name='d_jkfin')\n",
    "\n",
    "y_ijk=m.addVars(data_loader_a1.F,data_loader_a1.F,data_loader_a1.C,vtype=gp.GRB.BINARY,name='y_ijk')\n",
    "v_ijk=m.addVars(data_loader_a1.F,data_loader_a1.F,data_loader_a1.C,vtype=gp.GRB.BINARY,name='v_ijk')\n",
    "\n",
    "# a1=m.addVar(1,vtype=gp.GRB.INTEGER,name='a1')\n",
    "# a2=m.addVar(2,vtype=gp.GRB.INTEGER,name='a2')\n",
    "\n",
    "m.ModelSense=GRB.MINIMIZE\n",
    "\n",
    "\n",
    "# m.setObjectiveN(expr, index=0,pririty=1)\n",
    "m.setObjective(-z.sum())\n",
    "# m.setObjectiveN(gp.quicksum(gp.quicksum(data_loader_a1.DCost[k]*(gp.quicksum(d_jkfin[i,k]*data_loader_a1.arrivetime[i] for i in data_loader_a1.FD[t]) \n",
    "#                                                           - gp.quicksum(d_iksta[i,k]*data_loader_a1.leavetime[i] for i in data_loader_a1.FD[t]))\n",
    "#                                                           for k in data_loader_a1.C) for t in data_loader_a1.Dates ),index=1,priority=9)\n",
    "\n",
    "# m.setObjectiveN(x_ikdh.sum(),index=2,priority=8)\n",
    "# m.setObjectiveN(gp.quicksum(x_ikfo[i,k] for i in data_loader_a1.F for k in data_loader_a1.C2), index=3, priority=7)\n",
    "\n",
    "# m.setObjectiveN(a1-a2,index=4,priority=6)\n",
    "\n",
    "# m.setObjectiveN(-z.sum(),index = 0,weight =0.8)\n",
    "# m.setObjectiveN(x_ikdh.sum(),index=1,weight=0.2)\n",
    "# m.setObjectiveN(gp.quicksum(x_ikfo[i,k] for i in data_loader_a.F for k in data_loader_a.C2),index=2,weight=0.05)\n",
    "\n",
    "\n",
    "M=10000\n",
    "#对X的约束\n",
    "m.addConstrs(x_ikfo[i,k]==0 for i in data_loader_a1.F for k in data_loader_a1.C1 )\n",
    "m.addConstrs(x_ikcap[i,k]==0 for i in data_loader_a1.F for k in data_loader_a1.C3 )\n",
    "m.addConstrs(x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k]<=1 for i in data_loader_a1.F for k in data_loader_a1.C)\n",
    "\n",
    "#对Z的约束\n",
    "m.addConstrs(gp.quicksum(x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k] for k in data_loader_a1.C)<=M*(z[i]) for i in data_loader_a1.F)\n",
    "m.addConstrs(x_ikcap.sum(i,'*')== z[i] for i in data_loader_a1.F )\n",
    "m.addConstrs(x_ikfo.sum(i,'*')== z[i] for i in data_loader_a1.F )\n",
    "m.addConstrs(M*z[j]>=gp.quicksum(x_ikcap[j,k]+x_ikfo[j,k]+x_ikdh[j,k] for k in data_loader_a1.C) for j in data_loader_a1.F)\n",
    "\n",
    "#对Y的约束\n",
    "m.addConstrs(y_ijk[i,j,k]==0 for i,j in data_loader_a1.FF for k in data_loader_a1.C)\n",
    "#m.addConstrs(gp.quicksum(y_ijk[i,j,k] for j in F)<=x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k] for i in F for k in C)\n",
    "#m.addConstrs(gp.quicksum(y_ijk[j,i,k] for j in F)<=x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k] for i in F for k in C)\n",
    "\n",
    "#对roaster周期的约束\n",
    "# m.addConstrs(r_jkfin[j,k]== 0 for j in data_loader_a.nonF_arrive_base[0] for k in data_loader_a.C)\n",
    "m.addConstrs(r_iksta[i,k]== 0 for i in data_loader_a1.nonF_leave_base[0] for k in data_loader_a1.C)\n",
    "\n",
    "m.addConstrs(gp.quicksum(r_iksta[i,k] for i in data_loader_a1.F_leave_base[0] )<=1  for k in data_loader_a1.C)\n",
    "m.addConstrs(gp.quicksum(r_jkfin[j,k] for j in data_loader_a1.F )-gp.quicksum(r_iksta[i,k] for i in data_loader_a1.F_leave_base[0] ) == 0 for k in data_loader_a1.C)\n",
    "\n",
    "#第一问中航班对应周期的约束\n",
    "m.addConstrs(1-(y_ijk.sum(i,'*',k)+r_iksta[i,k]+r_jkfin[i,k])<=M*(1-(x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k])) for i in data_loader_a1.F for k in data_loader_a1.C)\n",
    "m.addConstrs(y_ijk.sum(i,'*',k)+r_jkfin[i,k]-(y_ijk.sum('*',i,k)+r_iksta[i,k]) == 0 for i in data_loader_a1.F for k in data_loader_a1.C)\n",
    "\n",
    "m.addConstrs(y_ijk.sum(i,'*',k)+r_jkfin[i,k] <= x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k]  for i in data_loader_a1.F for k in data_loader_a1.C)\n",
    "m.addConstrs(y_ijk.sum('*',i,k)+r_iksta[i,k] <= x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k]  for i in data_loader_a1.F for k in data_loader_a1.C)\n",
    "\n",
    "#第二问\n",
    "#对V的约束\n",
    "m.addConstrs(v_ijk[i,j,k]==0 for i,j in data_loader_a1.FF1 for k in data_loader_a1.C)\n",
    "m.addConstrs(v_ijk[i,j,k] <= y_ijk[i,j,k] for i in data_loader_a1.F for j in data_loader_a1.F for k in data_loader_a1.C)\n",
    "\n",
    "\n",
    "#对duty执勤的约束\n",
    "m.addConstrs(gp.quicksum(d_iksta[i,k] for i in data_loader_a1.FD[t]) - gp.quicksum(d_jkfin[i,k] for i in data_loader_a1.FD[t]) == 0 for i in data_loader_a1.F for k in data_loader_a1.C for t in data_loader_a1.Dates)\n",
    "m.addConstrs(gp.quicksum(d_iksta[i,k] for i in data_loader_a1.FD[t])   <= gp.quicksum(x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k] for i in data_loader_a1.FD[t]) for i in data_loader_a1.F for k in data_loader_a1.C for t in data_loader_a1.Dates)\n",
    "m.addConstrs(gp.quicksum(d_iksta[i,k] for i in data_loader_a1.FD[t])*M >= gp.quicksum(x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k] for i in data_loader_a1.FD[t]) for k in data_loader_a1.C for t in data_loader_a1.Dates)\n",
    "\n",
    "#m.addConstrs(d_iksta[i,k] for i in data_loader_a.FD[t]  <= x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k] for i in data_loader_a.FD[t] for k in data_loader_a.C for t in data_loader_a.Dates)\n",
    "\n",
    "#第二问中航班对应执勤，执勤对应周期的约束\n",
    "m.addConstrs((v_ijk.sum(i,'*',k)+ r_jkfin[i,k] ==  d_jkfin[i,k]) for i in data_loader_a1.F for k in data_loader_a1.C)\n",
    "m.addConstrs((v_ijk.sum('*',i,k)+ r_iksta[i,k] ==  d_iksta[i,k]) for i in data_loader_a1.F for k in data_loader_a1.C)\n",
    "\n",
    "#m.addConstrs((d_jkfin[i,k] <= x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k])  for i in data_loader_a.F for k in data_loader_a.C)\n",
    "#m.addConstrs((d_iksta[i,k] <= x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k])  for i in data_loader_a.F for k in data_loader_a.C)\n",
    "\n",
    "#第二问中其余约束\n",
    "m.addConstrs(gp.quicksum(d_jkfin[i,k]*data_loader_a1.arrivetime[i] for i in data_loader_a1.FD[t]) - gp.quicksum(d_iksta[i,k]*data_loader_a1.leavetime[i] for i in data_loader_a1.FD[t]) <= 720 for k in data_loader_a1.C for t in data_loader_a1.Dates )\n",
    "m.addConstrs(gp.quicksum((x_ikcap[i,k]+x_ikfo[i,k])*(data_loader_a1.leavetime[i]-data_loader_a1.arrivetime[i]) for i in data_loader_a1.FD[t]) <= 600 for k in data_loader_a1.C for t in data_loader_a1.Dates )\n",
    "#m.addConstrs(gp.quicksum(d_jkfin[i,k]*data_loader_a.arrivetime[i] for i in data_loader_a.FD[t]) - gp.quicksum(d_iksta[i,k]*data_loader_a.leavetime[i] for i in data_loader_a.FD[t]) >= 0 for k in data_loader_a.C for t in data_loader_a.Dates )\n",
    "\n",
    "\n",
    "# m.addConstr(z.sum('*')==108)\n",
    "# m.addConstr(gp.quicksum(gp.quicksum(data_loader_a1.DCost[k]*(gp.quicksum(d_jkfin[i,k]*data_loader_a1.arrivetime[i] for i in data_loader_a1.FD[t]) \n",
    "#                                                            - gp.quicksum(d_iksta[i,k]*data_loader_a1.leavetime[i] for i in data_loader_a1.FD[t]))\n",
    "#                                                            for k in data_loader_a1.C) for t in data_loader_a1.Dates )==[16900000,17000000])\n",
    "\n",
    "# m.addConstr(gp.quicksum(gp.quicksum(data_loader_a1.DCost[k]*(gp.quicksum(d_jkfin[i,k]*data_loader_a1.arrivetime[i] for i in data_loader_a1.FD[t])) \n",
    "#                     - gp.quicksum(d_iksta[i,k]*data_loader_a1.leavetime[i] for i in data_loader_a1.FD[t])\n",
    "#                 for k in data_loader_a1.C) for t in data_loader_a1.Dates) <= 170000000)\n",
    "#对执勤时长平衡的约束（辅助目标）\n",
    "# m.addConstrs(a1>=(gp.quicksum(gp.quicksum(d_jkfin[i,k]*data_loader_a1.arrivetime[i] for i in data_loader_a1.FD[t]) for t in data_loader_a1.Dates) - gp.quicksum(gp.quicksum(d_iksta[i,k]*data_loader_a1.leavetime[i] for i in data_loader_a1.FD[t]) for t in data_loader_a1.Dates)) for k in data_loader_a1.C)\n",
    "# m.addConstrs(a2<=(gp.quicksum(gp.quicksum(d_jkfin[i,k]*data_loader_a1.arrivetime[i] for i in data_loader_a1.FD[t]) for t in data_loader_a1.Dates) - gp.quicksum(gp.quicksum(d_iksta[i,k]*data_loader_a1.leavetime[i] for i in data_loader_a1.FD[t]) for t in data_loader_a1.Dates)) for k in data_loader_a1.C)\n",
    "#m.addConstr(a2 >= 0)\n",
    "# m.addContr(z.sum()==108)\n",
    "\n",
    "m.update()\n",
    "# m.setParam(\"MIPGap\", )\n",
    "m.optimize()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1fa0b3a6",
   "metadata": {},
   "source": [
    "### 联合优化目标②④⑦"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7c3b7001",
   "metadata": {},
   "outputs": [],
   "source": [
    "m=gp.Model('m1')\n",
    "\n",
    "z=m.addVars(data_loader_a1.F,vtype=gp.GRB.BINARY,name='z')\n",
    "x_ikdh=m.addVars(data_loader_a1.F,data_loader_a1.C,vtype=gp.GRB.BINARY,name='x_dh')\n",
    "x_ikfo=m.addVars(data_loader_a1.F,data_loader_a1.C,vtype=gp.GRB.BINARY,name='x_fo')\n",
    "x_ikcap=m.addVars(data_loader_a1.F,data_loader_a1.C,vtype=gp.GRB.BINARY,name='x_cap')\n",
    "\n",
    "r_iksta=m.addVars(data_loader_a1.F,data_loader_a1.C,vtype=gp.GRB.BINARY,name='r_iksta')\n",
    "r_jkfin=m.addVars(data_loader_a1.F,data_loader_a1.C,vtype=gp.GRB.BINARY,name='r_jkfin')\n",
    "d_iksta=m.addVars(data_loader_a1.F,data_loader_a1.C,vtype=gp.GRB.BINARY,name='d_iksta')\n",
    "d_jkfin=m.addVars(data_loader_a1.F,data_loader_a1.C,vtype=gp.GRB.BINARY,name='d_jkfin')\n",
    "\n",
    "y_ijk=m.addVars(data_loader_a1.F,data_loader_a1.F,data_loader_a1.C,vtype=gp.GRB.BINARY,name='y_ijk')\n",
    "v_ijk=m.addVars(data_loader_a1.F,data_loader_a1.F,data_loader_a1.C,vtype=gp.GRB.BINARY,name='v_ijk')\n",
    "\n",
    "# a1=m.addVar(1,vtype=gp.GRB.INTEGER,name='a1')\n",
    "# a2=m.addVar(2,vtype=gp.GRB.INTEGER,name='a2')\n",
    "\n",
    "m.ModelSense=GRB.MINIMIZE\n",
    "\n",
    "\n",
    "# m.setObjectiveN(expr, index=0,pririty=1)\n",
    "# m.setObjectiveN(-z.sum())\n",
    "m.setObjectiveN(gp.quicksum(gp.quicksum(data_loader_a1.DCost[k]*(gp.quicksum(d_jkfin[i,k]*data_loader_a1.arrivetime[i] for i in data_loader_a1.FD[t]) \n",
    "                                                          - gp.quicksum(d_iksta[i,k]*data_loader_a1.leavetime[i] for i in data_loader_a1.FD[t]))/60\n",
    "                                                          for k in data_loader_a1.C) for t in data_loader_a1.Dates ),index=0,priority=8)\n",
    "\n",
    "m.setObjectiveN(x_ikdh.sum(),index=1,priority=7)\n",
    "m.setObjectiveN(gp.quicksum(x_ikfo[i,k] for i in data_loader_a1.F for k in data_loader_a1.C2), index=2, priority=6)\n",
    "\n",
    "# m.setObjectiveN(a1-a2,index=3,priority=5)\n",
    "\n",
    "# m.setObjectiveN(-z.sum(),index = 0,weight =0.8)\n",
    "# m.setObjectiveN(x_ikdh.sum(),index=1,weight=0.2)\n",
    "# m.setObjectiveN(gp.quicksum(x_ikfo[i,k] for i in data_loader_a.F for k in data_loader_a.C2),index=2,weight=0.05)\n",
    "\n",
    "\n",
    "M=10000\n",
    "#对X的约束\n",
    "m.addConstrs(x_ikfo[i,k]==0 for i in data_loader_a1.F for k in data_loader_a1.C1 )\n",
    "m.addConstrs(x_ikcap[i,k]==0 for i in data_loader_a1.F for k in data_loader_a1.C3 )\n",
    "m.addConstrs(x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k]<=1 for i in data_loader_a1.F for k in data_loader_a1.C)\n",
    "\n",
    "#对Z的约束\n",
    "m.addConstrs(gp.quicksum(x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k] for k in data_loader_a1.C)<=M*(z[i]) for i in data_loader_a1.F)\n",
    "m.addConstrs(x_ikcap.sum(i,'*')== z[i] for i in data_loader_a1.F )\n",
    "m.addConstrs(x_ikfo.sum(i,'*')== z[i] for i in data_loader_a1.F )\n",
    "m.addConstrs(M*z[j]>=gp.quicksum(x_ikcap[j,k]+x_ikfo[j,k]+x_ikdh[j,k] for k in data_loader_a1.C) for j in data_loader_a1.F)\n",
    "\n",
    "#对Y的约束\n",
    "m.addConstrs(y_ijk[i,j,k]==0 for i,j in data_loader_a1.FF for k in data_loader_a1.C)\n",
    "#m.addConstrs(gp.quicksum(y_ijk[i,j,k] for j in F)<=x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k] for i in F for k in C)\n",
    "#m.addConstrs(gp.quicksum(y_ijk[j,i,k] for j in F)<=x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k] for i in F for k in C)\n",
    "\n",
    "#对roaster周期的约束\n",
    "# m.addConstrs(r_jkfin[j,k]== 0 for j in data_loader_a.nonF_arrive_base[0] for k in data_loader_a.C)\n",
    "m.addConstrs(r_iksta[i,k]== 0 for i in data_loader_a1.nonF_leave_base[0] for k in data_loader_a1.C)\n",
    "\n",
    "m.addConstrs(gp.quicksum(r_iksta[i,k] for i in data_loader_a1.F_leave_base[0] )<=1  for k in data_loader_a1.C)\n",
    "m.addConstrs(gp.quicksum(r_jkfin[j,k] for j in data_loader_a1.F )-gp.quicksum(r_iksta[i,k] for i in data_loader_a1.F_leave_base[0] ) == 0 for k in data_loader_a1.C)\n",
    "\n",
    "#第一问中航班对应周期的约束\n",
    "m.addConstrs(1-(y_ijk.sum(i,'*',k)+r_iksta[i,k]+r_jkfin[i,k])<=M*(1-(x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k])) for i in data_loader_a1.F for k in data_loader_a1.C)\n",
    "m.addConstrs(y_ijk.sum(i,'*',k)+r_jkfin[i,k]-(y_ijk.sum('*',i,k)+r_iksta[i,k]) == 0 for i in data_loader_a1.F for k in data_loader_a1.C)\n",
    "\n",
    "m.addConstrs(y_ijk.sum(i,'*',k)+r_jkfin[i,k] <= x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k]  for i in data_loader_a1.F for k in data_loader_a1.C)\n",
    "m.addConstrs(y_ijk.sum('*',i,k)+r_iksta[i,k] <= x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k]  for i in data_loader_a1.F for k in data_loader_a1.C)\n",
    "\n",
    "#第二问\n",
    "#对V的约束\n",
    "m.addConstrs(v_ijk[i,j,k]==0 for i,j in data_loader_a1.FF1 for k in data_loader_a1.C)\n",
    "m.addConstrs(v_ijk[i,j,k] <= y_ijk[i,j,k] for i in data_loader_a1.F for j in data_loader_a1.F for k in data_loader_a1.C)\n",
    "\n",
    "\n",
    "#对duty执勤的约束\n",
    "m.addConstrs(gp.quicksum(d_iksta[i,k] for i in data_loader_a1.FD[t]) - gp.quicksum(d_jkfin[i,k] for i in data_loader_a1.FD[t]) == 0 for i in data_loader_a1.F for k in data_loader_a1.C for t in data_loader_a1.Dates)\n",
    "m.addConstrs(gp.quicksum(d_iksta[i,k] for i in data_loader_a1.FD[t])   <= gp.quicksum(x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k] for i in data_loader_a1.FD[t]) for i in data_loader_a1.F for k in data_loader_a1.C for t in data_loader_a1.Dates)\n",
    "m.addConstrs(gp.quicksum(d_iksta[i,k] for i in data_loader_a1.FD[t])*M >= gp.quicksum(x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k] for i in data_loader_a1.FD[t]) for k in data_loader_a1.C for t in data_loader_a1.Dates)\n",
    "\n",
    "#m.addConstrs(d_iksta[i,k] for i in data_loader_a.FD[t]  <= x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k] for i in data_loader_a.FD[t] for k in data_loader_a.C for t in data_loader_a.Dates)\n",
    "\n",
    "#第二问中航班对应执勤，执勤对应周期的约束\n",
    "m.addConstrs((v_ijk.sum(i,'*',k)+ r_jkfin[i,k] ==  d_jkfin[i,k]) for i in data_loader_a1.F for k in data_loader_a1.C)\n",
    "m.addConstrs((v_ijk.sum('*',i,k)+ r_iksta[i,k] ==  d_iksta[i,k]) for i in data_loader_a1.F for k in data_loader_a1.C)\n",
    "\n",
    "#m.addConstrs((d_jkfin[i,k] <= x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k])  for i in data_loader_a.F for k in data_loader_a.C)\n",
    "#m.addConstrs((d_iksta[i,k] <= x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k])  for i in data_loader_a.F for k in data_loader_a.C)\n",
    "\n",
    "#第二问中其余约束\n",
    "m.addConstrs(gp.quicksum(d_jkfin[i,k]*data_loader_a1.arrivetime[i] for i in data_loader_a1.FD[t]) - gp.quicksum(d_iksta[i,k]*data_loader_a1.leavetime[i] for i in data_loader_a1.FD[t]) <= 720 for k in data_loader_a1.C for t in data_loader_a1.Dates )\n",
    "m.addConstrs(gp.quicksum((x_ikcap[i,k]+x_ikfo[i,k])*(data_loader_a1.leavetime[i]-data_loader_a1.arrivetime[i]) for i in data_loader_a1.FD[t]) <= 600 for k in data_loader_a1.C for t in data_loader_a1.Dates )\n",
    "#m.addConstrs(gp.quicksum(d_jkfin[i,k]*data_loader_a.arrivetime[i] for i in data_loader_a.FD[t]) - gp.quicksum(d_iksta[i,k]*data_loader_a.leavetime[i] for i in data_loader_a.FD[t]) >= 0 for k in data_loader_a.C for t in data_loader_a.Dates )\n",
    "\n",
    "\n",
    "# m.addConstr(z.sum('*')==108)\n",
    "# m.addConstr(gp.quicksum(gp.quicksum(data_loader_a1.DCost[k]*(gp.quicksum(d_jkfin[i,k]*data_loader_a1.arrivetime[i] for i in data_loader_a1.FD[t]) \n",
    "#                                                            - gp.quicksum(d_iksta[i,k]*data_loader_a1.leavetime[i] for i in data_loader_a1.FD[t]))\n",
    "#                                                            for k in data_loader_a1.C) for t in data_loader_a1.Dates )==[16900000,17000000])\n",
    "\n",
    "# m.addConstr(gp.quicksum(gp.quicksum(data_loader_a1.DCost[k]*(gp.quicksum(d_jkfin[i,k]*data_loader_a1.arrivetime[i] for i in data_loader_a1.FD[t])) \n",
    "#                     - gp.quicksum(d_iksta[i,k]*data_loader_a1.leavetime[i] for i in data_loader_a1.FD[t])\n",
    "#                 for k in data_loader_a1.C) for t in data_loader_a1.Dates) <= 170000000)\n",
    "#对执勤时长平衡的约束（辅助目标）\n",
    "# m.addConstrs(a1>=(gp.quicksum(gp.quicksum(d_jkfin[i,k]*data_loader_a1.arrivetime[i] for i in data_loader_a1.FD[t]) for t in data_loader_a1.Dates) - gp.quicksum(gp.quicksum(d_iksta[i,k]*data_loader_a1.leavetime[i] for i in data_loader_a1.FD[t]) for t in data_loader_a1.Dates)) for k in data_loader_a1.C)\n",
    "# m.addConstrs(a2<=(gp.quicksum(gp.quicksum(d_jkfin[i,k]*data_loader_a1.arrivetime[i] for i in data_loader_a1.FD[t]) for t in data_loader_a1.Dates) - gp.quicksum(gp.quicksum(d_iksta[i,k]*data_loader_a1.leavetime[i] for i in data_loader_a1.FD[t]) for t in data_loader_a1.Dates)) for k in data_loader_a1.C)\n",
    "#m.addConstr(a2 >= 0)\n",
    "m.addConstr(z.sum()==108)\n",
    "\n",
    "m.update()\n",
    "# m.setParam(\"MIPGap\", 0.0176)\n",
    "m.optimize()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "997f755b",
   "metadata": {},
   "source": [
    "### 优化目标⑤"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7e0642fd",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Using license file C:\\Users\\Ethan\\gurobi.lic\n",
      "Changed value of parameter MIPGap to 0.0176\n",
      "   Prev: 0.0001  Min: 0.0  Max: inf  Default: 0.0001\n",
      "Gurobi Optimizer version 9.1.2 build v9.1.2rc0 (win64)\n",
      "Thread count: 8 physical cores, 16 logical processors, using up to 16 threads\n",
      "Optimize a model with 723427 rows, 505874 columns and 4202577 nonzeros\n",
      "Model fingerprint: 0x4a84fd8c\n",
      "Variable types: 0 continuous, 505874 integer (505872 binary)\n",
      "Coefficient statistics:\n",
      "  Matrix range     [1e+00, 1e+05]\n",
      "  Objective range  [1e+00, 1e+00]\n",
      "  Bounds range     [1e+00, 2e+00]\n",
      "  RHS range        [1e+00, 3e+05]\n",
      "Presolve removed 678731 rows and 428550 columns\n",
      "Presolve time: 4.89s\n",
      "Presolved: 44696 rows, 77324 columns, 337995 nonzeros\n",
      "Variable types: 0 continuous, 77324 integer (77322 binary)\n",
      "\n",
      "Deterministic concurrent LP optimizer: primal and dual simplex\n",
      "Showing first log only...\n",
      "\n",
      "\n",
      "Root simplex log...\n",
      "\n",
      "Iteration    Objective       Primal Inf.    Dual Inf.      Time\n",
      "       0    0.0000000e+00   2.694125e+03   1.989273e+10      6s\n",
      "   48984    0.0000000e+00   7.426161e+01   2.128726e+10     10s\n",
      "   74786    0.0000000e+00   6.214788e+01   2.629687e+10     15s\n",
      "   93510    0.0000000e+00   5.650458e+01   8.761351e+09     20s\n",
      "  110250    0.0000000e+00   5.117866e+01   7.477045e+09     25s\n",
      "  124758    0.0000000e+00   4.646353e+01   1.480312e+10     30s\n",
      "  138522    0.0000000e+00   4.369952e+01   1.314540e+10     35s\n",
      "  152286    0.0000000e+00   4.173418e+01   1.129831e+10     40s\n",
      "  166174    0.0000000e+00   3.954355e+01   2.911671e+10     45s\n",
      "  179566    0.0000000e+00   3.529505e+01   1.868342e+10     50s\n",
      "  192834    0.0000000e+00   3.133985e+01   4.652866e+10     55s\n",
      "  206846    0.0000000e+00   2.805932e+01   3.114588e+10     60s\n",
      "  219122    0.0000000e+00   2.643633e+01   1.175611e+10     65s\n",
      "  230282    0.0000000e+00   2.467605e+01   1.224044e+10     70s\n",
      "  242558    0.0000000e+00   2.233750e+01   8.263275e+09     75s\n",
      "  254214    0.0000000e+00   1.997059e+01   1.982175e+10     80s\n",
      "  266242    0.0000000e+00   1.783557e+01   1.317129e+10     85s\n",
      "  277402    0.0000000e+00   1.575942e+01   6.110877e+10     90s\n",
      "  288438    0.0000000e+00   1.449256e+01   4.025392e+10     95s\n",
      "  299474    0.0000000e+00   1.293611e+01   1.886609e+10    100s\n",
      "  310758    0.0000000e+00   1.129687e+01   3.449002e+10    105s\n",
      "  321050    0.0000000e+00   9.878534e+00   2.020365e+10    110s\n",
      "  332582    0.0000000e+00   8.832444e+00   1.239667e+10    115s\n",
      "  342502    0.0000000e+00   7.984478e+00   1.866468e+10    120s\n",
      "  351518    0.0000000e+00   7.147777e+00   1.579835e+10    125s\n",
      "  360390    0.0000000e+00   6.298763e+00   1.102138e+10    130s\n",
      "  369238    0.0000000e+00   5.309814e+00   2.369920e+10    135s\n",
      "  377732    0.0000000e+00   4.889571e+00   8.355212e+09    140s\n",
      "  386202    0.0000000e+00   4.470233e+00   1.037626e+10    145s\n",
      "  393004    0.0000000e+00   4.208493e+00   8.269241e+10    150s\n",
      "  394804    0.0000000e+00   4.143278e+00   1.581862e+10    155s\n",
      "  396614    0.0000000e+00   4.061842e+00   5.755529e+11    160s\n",
      "  398344    0.0000000e+00   3.981903e+00   4.110739e+10    165s\n",
      "  400104    0.0000000e+00   3.905606e+00   6.667399e+10    170s\n",
      "  401814    0.0000000e+00   3.800468e+00   3.020018e+11    175s\n",
      "  403544    0.0000000e+00   3.685386e+00   8.490404e+10    180s\n",
      "  405424    0.0000000e+00   3.557672e+00   2.064319e+11    185s\n",
      "  407104    0.0000000e+00   3.464477e+00   1.224760e+11    190s\n",
      "  408924    0.0000000e+00   3.339180e+00   4.988202e+10    195s\n",
      "  410764    0.0000000e+00   3.239502e+00   2.284360e+11    200s\n",
      "  412484    0.0000000e+00   3.139387e+00   1.386338e+10    205s\n",
      "  414194    0.0000000e+00   3.051216e+00   5.042418e+10    210s\n",
      "  416014    0.0000000e+00   2.945611e+00   2.022482e+10    215s\n",
      "  417704    0.0000000e+00   2.850732e+00   1.140777e+11    220s\n",
      "  419474    0.0000000e+00   2.709832e+00   3.167061e+10    225s\n",
      "  421224    0.0000000e+00   2.586972e+00   3.533643e+10    230s\n",
      "  422964    0.0000000e+00   2.498197e+00   1.430665e+10    235s\n",
      "  424644    0.0000000e+00   2.429175e+00   4.175283e+10    240s\n",
      "  426354    0.0000000e+00   2.337044e+00   1.854235e+10    245s\n",
      "  427994    0.0000000e+00   2.245335e+00   1.345265e+11    250s\n",
      "  429774    0.0000000e+00   2.179268e+00   4.047466e+10    255s\n",
      "  431524    0.0000000e+00   2.098762e+00   1.268301e+11    260s\n",
      "  433244    0.0000000e+00   2.001790e+00   9.366056e+10    265s\n",
      "  434914    0.0000000e+00   1.902993e+00   7.272468e+11    270s\n",
      "  436684    0.0000000e+00   1.797489e+00   1.754926e+11    275s\n",
      "  438344    0.0000000e+00   1.712301e+00   4.129406e+10    280s\n",
      "  440094    0.0000000e+00   1.617605e+00   8.163180e+09    285s\n",
      "  441744    0.0000000e+00   1.496265e+00   2.342420e+10    290s\n",
      "  443424    0.0000000e+00   1.396125e+00   1.011845e+11    295s\n",
      "  445164    0.0000000e+00   1.304971e+00   2.060263e+10    300s\n",
      "  446914    0.0000000e+00   1.214544e+00   4.288058e+09    305s\n",
      "  448604    0.0000000e+00   1.134125e+00   3.879755e+10    310s\n",
      "  450354    0.0000000e+00   1.018079e+00   7.149654e+10    315s\n",
      "  451984    0.0000000e+00   9.409811e-01   1.991460e+10    320s\n",
      "  453564    0.0000000e+00   8.641068e-01   4.744092e+10    325s\n",
      "  455254    0.0000000e+00   7.849933e-01   6.968886e+09    330s\n",
      "  456914    0.0000000e+00   6.756544e-01   1.587303e+10    335s\n",
      "  458524    0.0000000e+00   6.210050e-01   5.084983e+10    340s\n",
      "  460164    0.0000000e+00   5.577478e-01   2.770623e+11    345s\n",
      "  461924    0.0000000e+00   4.965957e-01   3.483218e+09    350s\n",
      "  463604    0.0000000e+00   3.915217e-01   5.147119e+09    355s\n",
      "  465384    0.0000000e+00   3.292307e-01   9.647189e+10    360s\n",
      "  467164    0.0000000e+00   2.762524e-01   1.178721e+10    365s\n",
      "  468834    0.0000000e+00   2.151605e-01   7.372987e+10    370s\n",
      "  470694    0.0000000e+00   1.525577e-01   1.296236e+11    375s\n",
      "  472384    0.0000000e+00   9.996495e-02   9.711693e+09    380s\n",
      "  474144    0.0000000e+00   5.297063e-02   8.639255e+09    385s\n",
      "  476062    4.8174977e-07   1.252333e+04   0.000000e+00    390s\n",
      "  478404    3.3088021e-04   3.810873e+04   0.000000e+00    395s\n",
      "  481124    4.6035826e-04   5.835491e+04   0.000000e+00    400s\n",
      "  483614    6.3206499e-04   1.716410e+05   0.000000e+00    405s\n",
      "  486712    1.2138720e-03   8.535438e+04   0.000000e+00    410s\n",
      "  491074    2.1107135e-03   1.578860e+04   0.000000e+00    415s\n",
      "  494088    2.5362263e-03   4.355863e+04   0.000000e+00    420s\n",
      "  495778    3.0866104e-03   4.586105e+04   0.000000e+00    425s\n",
      "  497468    3.7986231e-03   2.513373e+05   0.000000e+00    430s\n",
      "  499288    4.5944009e-03   1.113679e+05   0.000000e+00    435s\n",
      "  501248    5.5642080e-03   1.010102e+05   0.000000e+00    440s\n",
      "  503098    6.3306574e-03   5.803764e+04   0.000000e+00    445s\n",
      "  505018    7.0562597e-03   1.247818e+05   0.000000e+00    450s\n",
      "  507188    7.9465849e-03   3.382100e+04   0.000000e+00    455s\n",
      "  508928    8.6879824e-03   3.380781e+05   0.000000e+00    460s\n",
      "  510658    9.4110222e-03   1.119903e+05   0.000000e+00    465s\n",
      "  512418    1.0321900e-02   2.252196e+04   0.000000e+00    470s\n",
      "  514228    1.1199601e-02   1.057666e+04   0.000000e+00    475s\n",
      "  516108    1.2180799e-02   7.482085e+04   0.000000e+00    480s\n",
      "  518208    1.3546190e-02   1.710028e+04   0.000000e+00    485s\n",
      "  520068    1.4845988e-02   2.035778e+04   0.000000e+00    490s\n"
     ]
    }
   ],
   "source": [
    "m=gp.Model('m1')\n",
    "\n",
    "z=m.addVars(data_loader_a1.F,vtype=gp.GRB.BINARY,name='z')\n",
    "x_ikdh=m.addVars(data_loader_a1.F,data_loader_a1.C,vtype=gp.GRB.BINARY,name='x_dh')\n",
    "x_ikfo=m.addVars(data_loader_a1.F,data_loader_a1.C,vtype=gp.GRB.BINARY,name='x_fo')\n",
    "x_ikcap=m.addVars(data_loader_a1.F,data_loader_a1.C,vtype=gp.GRB.BINARY,name='x_cap')\n",
    "\n",
    "r_iksta=m.addVars(data_loader_a1.F,data_loader_a1.C,vtype=gp.GRB.BINARY,name='r_iksta')\n",
    "r_jkfin=m.addVars(data_loader_a1.F,data_loader_a1.C,vtype=gp.GRB.BINARY,name='r_jkfin')\n",
    "d_iksta=m.addVars(data_loader_a1.F,data_loader_a1.C,vtype=gp.GRB.BINARY,name='d_iksta')\n",
    "d_jkfin=m.addVars(data_loader_a1.F,data_loader_a1.C,vtype=gp.GRB.BINARY,name='d_jkfin')\n",
    "\n",
    "y_ijk=m.addVars(data_loader_a1.F,data_loader_a1.F,data_loader_a1.C,vtype=gp.GRB.BINARY,name='y_ijk')\n",
    "v_ijk=m.addVars(data_loader_a1.F,data_loader_a1.F,data_loader_a1.C,vtype=gp.GRB.BINARY,name='v_ijk')\n",
    "\n",
    "a1=m.addVar(1,vtype=gp.GRB.INTEGER,name='a1')\n",
    "a2=m.addVar(2,vtype=gp.GRB.INTEGER,name='a2')\n",
    "\n",
    "m.ModelSense=GRB.MINIMIZE\n",
    "\n",
    "\n",
    "# m.setObjectiveN(expr, index=0,pririty=1)\n",
    "# m.setObjectiveN(-z.sum())\n",
    "# m.setObjectiveN(gp.quicksum(gp.quicksum(data_loader_a1.DCost[k]*(gp.quicksum(d_jkfin[i,k]*data_loader_a1.arrivetime[i] for i in data_loader_a1.FD[t]) \n",
    "#                                                           - gp.quicksum(d_iksta[i,k]*data_loader_a1.leavetime[i] for i in data_loader_a1.FD[t]))/60\n",
    "#                                                           for k in data_loader_a1.C) for t in data_loader_a1.Dates ),index=0,priority=8)\n",
    "\n",
    "# m.setObjectiveN(x_ikdh.sum(),index=1,priority=7)\n",
    "# m.setObjectiveN(gp.quicksum(x_ikfo[i,k] for i in data_loader_a1.F for k in data_loader_a1.C2), index=2, priority=6)\n",
    "\n",
    "m.setObjective(a1-a2)\n",
    "\n",
    "# m.setObjectiveN(-z.sum(),index = 0,weight =0.8)\n",
    "# m.setObjectiveN(x_ikdh.sum(),index=1,weight=0.2)\n",
    "# m.setObjectiveN(gp.quicksum(x_ikfo[i,k] for i in data_loader_a.F for k in data_loader_a.C2),index=2,weight=0.05)\n",
    "\n",
    "\n",
    "M=10000\n",
    "#对X的约束\n",
    "m.addConstrs(x_ikfo[i,k]==0 for i in data_loader_a1.F for k in data_loader_a1.C1 )\n",
    "m.addConstrs(x_ikcap[i,k]==0 for i in data_loader_a1.F for k in data_loader_a1.C3 )\n",
    "m.addConstrs(x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k]<=1 for i in data_loader_a1.F for k in data_loader_a1.C)\n",
    "\n",
    "#对Z的约束\n",
    "m.addConstrs(gp.quicksum(x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k] for k in data_loader_a1.C)<=M*(z[i]) for i in data_loader_a1.F)\n",
    "m.addConstrs(x_ikcap.sum(i,'*')== z[i] for i in data_loader_a1.F )\n",
    "m.addConstrs(x_ikfo.sum(i,'*')== z[i] for i in data_loader_a1.F )\n",
    "m.addConstrs(M*z[j]>=gp.quicksum(x_ikcap[j,k]+x_ikfo[j,k]+x_ikdh[j,k] for k in data_loader_a1.C) for j in data_loader_a1.F)\n",
    "\n",
    "#对Y的约束\n",
    "m.addConstrs(y_ijk[i,j,k]==0 for i,j in data_loader_a1.FF for k in data_loader_a1.C)\n",
    "#m.addConstrs(gp.quicksum(y_ijk[i,j,k] for j in F)<=x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k] for i in F for k in C)\n",
    "#m.addConstrs(gp.quicksum(y_ijk[j,i,k] for j in F)<=x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k] for i in F for k in C)\n",
    "\n",
    "#对roaster周期的约束\n",
    "# m.addConstrs(r_jkfin[j,k]== 0 for j in data_loader_a.nonF_arrive_base[0] for k in data_loader_a.C)\n",
    "m.addConstrs(r_iksta[i,k]== 0 for i in data_loader_a1.nonF_leave_base[0] for k in data_loader_a1.C)\n",
    "\n",
    "m.addConstrs(gp.quicksum(r_iksta[i,k] for i in data_loader_a1.F_leave_base[0] )<=1  for k in data_loader_a1.C)\n",
    "m.addConstrs(gp.quicksum(r_jkfin[j,k] for j in data_loader_a1.F )-gp.quicksum(r_iksta[i,k] for i in data_loader_a1.F_leave_base[0] ) == 0 for k in data_loader_a1.C)\n",
    "\n",
    "#第一问中航班对应周期的约束\n",
    "m.addConstrs(1-(y_ijk.sum(i,'*',k)+r_iksta[i,k]+r_jkfin[i,k])<=M*(1-(x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k])) for i in data_loader_a1.F for k in data_loader_a1.C)\n",
    "m.addConstrs(y_ijk.sum(i,'*',k)+r_jkfin[i,k]-(y_ijk.sum('*',i,k)+r_iksta[i,k]) == 0 for i in data_loader_a1.F for k in data_loader_a1.C)\n",
    "\n",
    "m.addConstrs(y_ijk.sum(i,'*',k)+r_jkfin[i,k] <= x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k]  for i in data_loader_a1.F for k in data_loader_a1.C)\n",
    "m.addConstrs(y_ijk.sum('*',i,k)+r_iksta[i,k] <= x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k]  for i in data_loader_a1.F for k in data_loader_a1.C)\n",
    "\n",
    "#第二问\n",
    "#对V的约束\n",
    "m.addConstrs(v_ijk[i,j,k]==0 for i,j in data_loader_a1.FF1 for k in data_loader_a1.C)\n",
    "m.addConstrs(v_ijk[i,j,k] <= y_ijk[i,j,k] for i in data_loader_a1.F for j in data_loader_a1.F for k in data_loader_a1.C)\n",
    "\n",
    "\n",
    "#对duty执勤的约束\n",
    "m.addConstrs(gp.quicksum(d_iksta[i,k] for i in data_loader_a1.FD[t]) - gp.quicksum(d_jkfin[i,k] for i in data_loader_a1.FD[t]) == 0 for i in data_loader_a1.F for k in data_loader_a1.C for t in data_loader_a1.Dates)\n",
    "m.addConstrs(gp.quicksum(d_iksta[i,k] for i in data_loader_a1.FD[t])   <= gp.quicksum(x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k] for i in data_loader_a1.FD[t]) for i in data_loader_a1.F for k in data_loader_a1.C for t in data_loader_a1.Dates)\n",
    "m.addConstrs(gp.quicksum(d_iksta[i,k] for i in data_loader_a1.FD[t])*M >= gp.quicksum(x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k] for i in data_loader_a1.FD[t]) for k in data_loader_a1.C for t in data_loader_a1.Dates)\n",
    "\n",
    "#m.addConstrs(d_iksta[i,k] for i in data_loader_a.FD[t]  <= x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k] for i in data_loader_a.FD[t] for k in data_loader_a.C for t in data_loader_a.Dates)\n",
    "\n",
    "#第二问中航班对应执勤，执勤对应周期的约束\n",
    "m.addConstrs((v_ijk.sum(i,'*',k)+ r_jkfin[i,k] ==  d_jkfin[i,k]) for i in data_loader_a1.F for k in data_loader_a1.C)\n",
    "m.addConstrs((v_ijk.sum('*',i,k)+ r_iksta[i,k] ==  d_iksta[i,k]) for i in data_loader_a1.F for k in data_loader_a1.C)\n",
    "\n",
    "#m.addConstrs((d_jkfin[i,k] <= x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k])  for i in data_loader_a.F for k in data_loader_a.C)\n",
    "#m.addConstrs((d_iksta[i,k] <= x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k])  for i in data_loader_a.F for k in data_loader_a.C)\n",
    "\n",
    "#第二问中其余约束\n",
    "m.addConstrs(gp.quicksum(d_jkfin[i,k]*data_loader_a1.arrivetime[i] for i in data_loader_a1.FD[t]) - gp.quicksum(d_iksta[i,k]*data_loader_a1.leavetime[i] for i in data_loader_a1.FD[t]) <= 720 for k in data_loader_a1.C for t in data_loader_a1.Dates )\n",
    "m.addConstrs(gp.quicksum((x_ikcap[i,k]+x_ikfo[i,k])*(data_loader_a1.leavetime[i]-data_loader_a1.arrivetime[i]) for i in data_loader_a1.FD[t]) <= 600 for k in data_loader_a1.C for t in data_loader_a1.Dates )\n",
    "#m.addConstrs(gp.quicksum(d_jkfin[i,k]*data_loader_a.arrivetime[i] for i in data_loader_a.FD[t]) - gp.quicksum(d_iksta[i,k]*data_loader_a.leavetime[i] for i in data_loader_a.FD[t]) >= 0 for k in data_loader_a.C for t in data_loader_a.Dates )\n",
    "\n",
    "\n",
    "# m.addConstr(z.sum('*')==108)\n",
    "# m.addConstr(gp.quicksum(gp.quicksum(data_loader_a1.DCost[k]*(gp.quicksum(d_jkfin[i,k]*data_loader_a1.arrivetime[i] for i in data_loader_a1.FD[t]) \n",
    "#                                                            - gp.quicksum(d_iksta[i,k]*data_loader_a1.leavetime[i] for i in data_loader_a1.FD[t]))\n",
    "#                                                            for k in data_loader_a1.C) for t in data_loader_a1.Dates )==[16900000,17000000])\n",
    "\n",
    "# m.addConstr(gp.quicksum(gp.quicksum(data_loader_a1.DCost[k]*(gp.quicksum(d_jkfin[i,k]*data_loader_a1.arrivetime[i] for i in data_loader_a1.FD[t])) \n",
    "#                     - gp.quicksum(d_iksta[i,k]*data_loader_a1.leavetime[i] for i in data_loader_a1.FD[t])\n",
    "#                 for k in data_loader_a1.C) for t in data_loader_a1.Dates) <= 170000000)\n",
    "#对执勤时长平衡的约束（辅助目标）\n",
    "m.addConstrs(a1>=(gp.quicksum(gp.quicksum(d_jkfin[i,k]*data_loader_a1.arrivetime[i] for i in data_loader_a1.FD[t]) for t in data_loader_a1.Dates) - gp.quicksum(gp.quicksum(d_iksta[i,k]*data_loader_a1.leavetime[i] for i in data_loader_a1.FD[t]) for t in data_loader_a1.Dates)) for k in data_loader_a1.C)\n",
    "m.addConstrs(a2<=(gp.quicksum(gp.quicksum(d_jkfin[i,k]*data_loader_a1.arrivetime[i] for i in data_loader_a1.FD[t]) for t in data_loader_a1.Dates) - gp.quicksum(gp.quicksum(d_iksta[i,k]*data_loader_a1.leavetime[i] for i in data_loader_a1.FD[t]) for t in data_loader_a1.Dates)) for k in data_loader_a1.C)\n",
    "#m.addConstr(a2 >= 0)\n",
    "m.addConstr(z.sum()==108)\n",
    "m.addConstr(gp.quicksum(gp.quicksum(data_loader_a1.DCost[k]*(gp.quicksum(d_jkfin[i,k]*data_loader_a1.arrivetime[i] for i in data_loader_a1.FD[t]) \n",
    "                                                          - gp.quicksum(d_iksta[i,k]*data_loader_a1.leavetime[i] for i in data_loader_a1.FD[t]))/60\n",
    "                                                          for k in data_loader_a1.C) for t in data_loader_a1.Dates) == 282710)\n",
    "m.addConstr(x_ikdh.sum()==15)\n",
    "m.addConstr(gp.quicksum(x_ikfo[i,k] for i in data_loader_a1.F for k in data_loader_a1.C2)==0)\n",
    "\n",
    "m.update()\n",
    "m.setParam(\"MIPGap\", 0.0176)\n",
    "m.optimize()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "78a434dc",
   "metadata": {},
   "source": [
    "## 为第二阶段准备数据"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a1e75944",
   "metadata": {},
   "outputs": [],
   "source": [
    "results_dic = {'em_no':[], 'fl_no':[], 'cls':[]}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c45ab355",
   "metadata": {},
   "outputs": [],
   "source": [
    "for i in data_loader_a1.F:\n",
    "    for k in data_loader_a1.C:\n",
    "        if x_ikcap[i,k].x > 0.9:\n",
    "            results_dic['em_no'].append(k)\n",
    "            for key, value in data_loader.F_dic.items():\n",
    "                if value == data_loader_a1.F_dic[i]:\n",
    "                    results_dic['fl_no'].append(key)\n",
    "                    break\n",
    "            results_dic['cls'].append(1)\n",
    "        if x_ikfo[i,k].x > 0.9:\n",
    "            results_dic['em_no'].append(k)\n",
    "            for key, value in data_loader.F_dic.items():\n",
    "                if value == data_loader_a1.F_dic[i]:\n",
    "                    results_dic['fl_no'].append(key)\n",
    "                    break\n",
    "            results_dic['cls'].append(2)\n",
    "        if x_ikdh[i,k].x > 0.9:\n",
    "            results_dic['em_no'].append(k)\n",
    "            for key, value in data_loader.F_dic.items():\n",
    "                if value == data_loader_a1.F_dic[i]:\n",
    "                    results_dic['fl_no'].append(key)\n",
    "                    break\n",
    "            results_dic['cls'].append(3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "645daf71",
   "metadata": {},
   "outputs": [],
   "source": [
    "leave_temp_base = {}\n",
    "arrive_time_skipping = {}\n",
    "for k in data_loader_a1.C:\n",
    "    for i in data_loader_a1.F:\n",
    "        if r_jkfin[i,k].x > 0.9:\n",
    "            leave_temp_base[k] = data_loader_a1.AP_dic[data_loader_a1.F_ap_arr_dic[i]]\n",
    "            arrive_time_skipping[k] =  data_loader_a1.arrivetime[i] - (24*60*(19-1) - data_loader_a1.get_min_time())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0466f552",
   "metadata": {},
   "outputs": [],
   "source": [
    "data_loader_a2 = DataLoader('data/Data A-Crew.csv', 'data/Data A-Flight.csv')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "716a3b1a",
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "data_loader_a2.dump_data(cropped_date=(19,25))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "134e4c8e",
   "metadata": {},
   "outputs": [],
   "source": [
    "for k in data_loader_a2.C:\n",
    "    for key, value in data_loader_a2.AP_dic.items():\n",
    "        if value == leave_temp_base[k]:\n",
    "            leave_temp_base[k] = key\n",
    "            break"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fc63a812",
   "metadata": {},
   "outputs": [],
   "source": [
    "leave_temp_base_dic = {}\n",
    "MinRest = 660\n",
    "for k in data_loader_a2.C:\n",
    "    for i in data_loader_a2.F:\n",
    "        if ((data_loader_a2.F_ap_dpt_dic[i] == leave_temp_base[k]) and ((data_loader_a2.leavetime[i]+data_loader_a2.get_min_time()-data_loader_a1.get_min_time() - arrive_time_skipping[k])>=MinRest)):#不是到达的机场\n",
    "            if k not in leave_temp_base_dic.keys(): \n",
    "                leave_temp_base_dic[k] = [i]\n",
    "            else:\n",
    "                leave_temp_base_dic[k].append(i)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e63dbc65",
   "metadata": {},
   "source": [
    "## 第二阶段"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b81b3938",
   "metadata": {},
   "source": [
    "### 优化目标①"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "97fd0033",
   "metadata": {},
   "outputs": [],
   "source": [
    "m=gp.Model('m1')\n",
    "\n",
    "z=m.addVars(data_loader_a2.F,vtype=gp.GRB.BINARY,name='z')\n",
    "x_ikdh=m.addVars(data_loader_a2.F,data_loader_a2.C,vtype=gp.GRB.BINARY,name='x_dh')\n",
    "x_ikfo=m.addVars(data_loader_a2.F,data_loader_a2.C,vtype=gp.GRB.BINARY,name='x_fo')\n",
    "x_ikcap=m.addVars(data_loader_a2.F,data_loader_a2.C,vtype=gp.GRB.BINARY,name='x_cap')\n",
    "\n",
    "r_iksta=m.addVars(data_loader_a2.F,data_loader_a2.C,vtype=gp.GRB.BINARY,name='r_iksta')\n",
    "r_jkfin=m.addVars(data_loader_a2.F,data_loader_a2.C,vtype=gp.GRB.BINARY,name='r_jkfin')\n",
    "d_iksta=m.addVars(data_loader_a2.F,data_loader_a2.C,vtype=gp.GRB.BINARY,name='d_iksta')\n",
    "d_jkfin=m.addVars(data_loader_a2.F,data_loader_a2.C,vtype=gp.GRB.BINARY,name='d_jkfin')\n",
    "\n",
    "y_ijk=m.addVars(data_loader_a2.F,data_loader_a2.F,data_loader_a2.C,vtype=gp.GRB.BINARY,name='y_ijk')\n",
    "v_ijk=m.addVars(data_loader_a2.F,data_loader_a2.F,data_loader_a2.C,vtype=gp.GRB.BINARY,name='v_ijk')\n",
    "\n",
    "# a1=m.addVar(1,vtype=gp.GRB.INTEGER,name='a1')\n",
    "# a2=m.addVar(2,vtype=gp.GRB.INTEGER,name='a2')\n",
    "\n",
    "m.ModelSense=GRB.MINIMIZE\n",
    "\n",
    "m.setObjective(-z.sum())\n",
    "\n",
    "#m.setObjective(x_ikdh.sum())\n",
    "# m.setObjective(gp.quicksum(x_ikfo[i,k] for i in F for k in C2))\n",
    "# m.setObjective(gp.quicksum(gp.quicksum(data_loader_a.DCost[k]*(gp.quicksum(d_jkfin[i,k]*data_loader_a.arrivetime[i] for i in data_loader_a.FD[t]) \n",
    "#                                                                - gp.quicksum(d_iksta[i,k]*data_loader_a.leavetime[i] for i in data_loader_a.FD[t]))/60\n",
    "#                                                            for k in data_loader_a.C) for t in data_loader_a.Dates ))\n",
    "# m.setObjective(a1-a2)\n",
    "\n",
    "#m.setObjectiveN(-z.sum(),index = 0,weight =0.8)\n",
    "#m.setObjectiveN(x_ikdh.sum(),index=1,weight=0.2)\n",
    "#m.setObjectiveN(gp.quicksum(x_ikfo[i,k] for i in data_loader_a.F for k in data_loader_a.C2),index=2,weight=0.05)\n",
    "\n",
    "\n",
    "M=10000\n",
    "#对X的约束\n",
    "m.addConstrs(x_ikfo[i,k]==0 for i in data_loader_a2.F for k in data_loader_a2.C1 )\n",
    "m.addConstrs(x_ikcap[i,k]==0 for i in data_loader_a2.F for k in data_loader_a2.C3 )\n",
    "m.addConstrs(x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k]<=1 for i in data_loader_a2.F for k in data_loader_a2.C)\n",
    "\n",
    "#对Z的约束\n",
    "m.addConstrs(gp.quicksum(x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k] for k in data_loader_a2.C)<=M*(z[i]) for i in data_loader_a2.F)\n",
    "m.addConstrs(x_ikcap.sum(i,'*')== z[i] for i in data_loader_a2.F )\n",
    "m.addConstrs(x_ikfo.sum(i,'*')== z[i] for i in data_loader_a2.F )\n",
    "m.addConstrs(M*z[j]>=gp.quicksum(x_ikcap[j,k]+x_ikfo[j,k]+x_ikdh[j,k] for k in data_loader_a2.C) for j in data_loader_a2.F)\n",
    "\n",
    "#对Y的约束\n",
    "m.addConstrs(y_ijk[i,j,k]==0 for i,j in data_loader_a2.FF for k in data_loader_a2.C)\n",
    "#m.addConstrs(gp.quicksum(y_ijk[i,j,k] for j in F)<=x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k] for i in F for k in C)\n",
    "#m.addConstrs(gp.quicksum(y_ijk[j,i,k] for j in F)<=x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k] for i in F for k in C)\n",
    "\n",
    "#对roaster周期的约束\n",
    "m.addConstrs(r_jkfin[j,k]== 0 for j in data_loader_a2.nonF_arrive_base[0] for k in data_loader_a2.C)\n",
    "# m.addConstrs(r_iksta[i,k]== 0 for i in non_leave_temp_base[k] for k in data_loader_a2.C)\n",
    "\n",
    "m.addConstrs(gp.quicksum(r_iksta[i,k] for i in data_loader_a2.F )<=1  for k in data_loader_a2.C)\n",
    "m.addConstrs(gp.quicksum(r_jkfin[j,k] for j in data_loader_a2.F )-gp.quicksum(r_iksta[i,k] for i in leave_temp_base_dic[k] ) == 0 for k in data_loader_a2.C)\n",
    "\n",
    "#第一问中航班对应周期的约束\n",
    "m.addConstrs(1-(y_ijk.sum(i,'*',k)+r_iksta[i,k]+r_jkfin[i,k])<=M*(1-(x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k])) for i in data_loader_a2.F for k in data_loader_a2.C)\n",
    "m.addConstrs(y_ijk.sum(i,'*',k)+r_jkfin[i,k]-(y_ijk.sum('*',i,k)+r_iksta[i,k]) == 0 for i in data_loader_a2.F for k in data_loader_a2.C)\n",
    "\n",
    "m.addConstrs(y_ijk.sum(i,'*',k)+r_jkfin[i,k] <= x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k]  for i in data_loader_a2.F for k in data_loader_a2.C)\n",
    "m.addConstrs(y_ijk.sum('*',i,k)+r_iksta[i,k] <= x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k]  for i in data_loader_a2.F for k in data_loader_a2.C)\n",
    "\n",
    "#第二问\n",
    "#对V的约束\n",
    "m.addConstrs(v_ijk[i,j,k]==0 for i,j in data_loader_a2.FF1 for k in data_loader_a2.C)\n",
    "m.addConstrs(v_ijk[i,j,k] <= y_ijk[i,j,k] for i in data_loader_a2.F for j in data_loader_a2.F for k in data_loader_a2.C)\n",
    "\n",
    "\n",
    "#对duty执勤的约束\n",
    "m.addConstrs(gp.quicksum(d_iksta[i,k] for i in data_loader_a2.FD[t]) - gp.quicksum(d_jkfin[i,k] for i in data_loader_a2.FD[t]) == 0 for i in data_loader_a2.F for k in data_loader_a2.C for t in data_loader_a2.Dates)\n",
    "m.addConstrs(gp.quicksum(d_iksta[i,k] for i in data_loader_a2.FD[t])   <= gp.quicksum(x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k] for i in data_loader_a2.FD[t]) for i in data_loader_a2.F for k in data_loader_a2.C for t in data_loader_a2.Dates)\n",
    "m.addConstrs(gp.quicksum(d_iksta[i,k] for i in data_loader_a2.FD[t])*M >= gp.quicksum(x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k] for i in data_loader_a2.FD[t]) for k in data_loader_a2.C for t in data_loader_a2.Dates)\n",
    "\n",
    "#m.addConstrs(d_iksta[i,k] for i in data_loader_a.FD[t]  <= x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k] for i in data_loader_a.FD[t] for k in data_loader_a.C for t in data_loader_a.Dates)\n",
    "\n",
    "#第二问中航班对应执勤，执勤对应周期的约束\n",
    "m.addConstrs((v_ijk.sum(i,'*',k)+ r_jkfin[i,k] ==  d_jkfin[i,k]) for i in data_loader_a2.F for k in data_loader_a2.C)\n",
    "m.addConstrs((v_ijk.sum('*',i,k)+ r_iksta[i,k] ==  d_iksta[i,k]) for i in data_loader_a2.F for k in data_loader_a2.C)\n",
    "\n",
    "#m.addConstrs((d_jkfin[i,k] <= x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k])  for i in data_loader_a.F for k in data_loader_a.C)\n",
    "#m.addConstrs((d_iksta[i,k] <= x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k])  for i in data_loader_a.F for k in data_loader_a.C)\n",
    "\n",
    "#第二问中其余约束\n",
    "m.addConstrs(gp.quicksum(d_jkfin[i,k]*data_loader_a2.arrivetime[i] for i in data_loader_a2.FD[t]) - gp.quicksum(d_iksta[i,k]*data_loader_a2.leavetime[i] for i in data_loader_a2.FD[t]) <= 720 for k in data_loader_a2.C for t in data_loader_a2.Dates )\n",
    "m.addConstrs(gp.quicksum((x_ikcap[i,k]+x_ikfo[i,k])*(data_loader_a2.leavetime[i]-data_loader_a2.arrivetime[i]) for i in data_loader_a2.FD[t]) <= 600 for k in data_loader_a2.C for t in data_loader_a2.Dates )\n",
    "#m.addConstrs(gp.quicksum(d_jkfin[i,k]*data_loader_a.arrivetime[i] for i in data_loader_a.FD[t]) - gp.quicksum(d_iksta[i,k]*data_loader_a.leavetime[i] for i in data_loader_a.FD[t]) >= 0 for k in data_loader_a.C for t in data_loader_a.Dates )\n",
    "\n",
    "\n",
    "#m.addConstr(z.sum('*')==206)\n",
    "# m.addConstr(gp.quicksum(gp.quicksum(data_loader_a.DCost[k]*(gp.quicksum(d_jkfin[i,k]*data_loader_a.arrivetime[i] for i in data_loader_a.FD[t]) \n",
    "#                     - gp.quicksum(d_iksta[i,k]*data_loader_a.leavetime[i] for i in data_loader_a.FD[t]))\n",
    "#                 for k in data_loader_a.C) for t in data_loader_a.Dates )==[1324573-100000,1324573+100000])\n",
    "#对执勤时长平衡的约束（辅助目标）\n",
    "# m.addConstrs(a1>=(gp.quicksum(gp.quicksum(d_jkfin[i,k]*data_loader_a.arrivetime[i] for i in data_loader_a.FD[t]) for t in data_loader_a.Dates) - gp.quicksum(gp.quicksum(d_iksta[i,k]*data_loader_a.leavetime[i] for i in data_loader_a.FD[t]) for t in data_loader_a.Dates)) for k in data_loader_a.C)\n",
    "# m.addConstrs(a2<=(gp.quicksum(gp.quicksum(d_jkfin[i,k]*data_loader_a.arrivetime[i] for i in data_loader_a.FD[t]) for t in data_loader_a.Dates) - gp.quicksum(gp.quicksum(d_iksta[i,k]*data_loader_a.leavetime[i] for i in data_loader_a.FD[t]) for t in data_loader_a.Dates)) for k in data_loader_a.C)\n",
    "         \n",
    "             \n",
    "m.update()\n",
    "m.optimize()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "911ae9ff",
   "metadata": {},
   "source": [
    "### 优化目标②"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c780adf4",
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "m=gp.Model('m1')\n",
    "\n",
    "z=m.addVars(data_loader_a2.F,vtype=gp.GRB.BINARY,name='z')\n",
    "x_ikdh=m.addVars(data_loader_a2.F,data_loader_a2.C,vtype=gp.GRB.BINARY,name='x_dh')\n",
    "x_ikfo=m.addVars(data_loader_a2.F,data_loader_a2.C,vtype=gp.GRB.BINARY,name='x_fo')\n",
    "x_ikcap=m.addVars(data_loader_a2.F,data_loader_a2.C,vtype=gp.GRB.BINARY,name='x_cap')\n",
    "\n",
    "r_iksta=m.addVars(data_loader_a2.F,data_loader_a2.C,vtype=gp.GRB.BINARY,name='r_iksta')\n",
    "r_jkfin=m.addVars(data_loader_a2.F,data_loader_a2.C,vtype=gp.GRB.BINARY,name='r_jkfin')\n",
    "d_iksta=m.addVars(data_loader_a2.F,data_loader_a2.C,vtype=gp.GRB.BINARY,name='d_iksta')\n",
    "d_jkfin=m.addVars(data_loader_a2.F,data_loader_a2.C,vtype=gp.GRB.BINARY,name='d_jkfin')\n",
    "\n",
    "y_ijk=m.addVars(data_loader_a2.F,data_loader_a2.F,data_loader_a2.C,vtype=gp.GRB.BINARY,name='y_ijk')\n",
    "v_ijk=m.addVars(data_loader_a2.F,data_loader_a2.F,data_loader_a2.C,vtype=gp.GRB.BINARY,name='v_ijk')\n",
    "\n",
    "# a1=m.addVar(1,vtype=gp.GRB.INTEGER,name='a1')\n",
    "# a2=m.addVar(2,vtype=gp.GRB.INTEGER,name='a2')\n",
    "\n",
    "m.ModelSense=GRB.MINIMIZE\n",
    "\n",
    "# m.setObjective(-z.sum())\n",
    "m.setObjective(gp.quicksum(gp.quicksum(data_loader_a2.DCost[k]*(gp.quicksum(d_jkfin[i,k]*data_loader_a2.arrivetime[i] for i in data_loader_a2.FD[t]) \n",
    "                                                          - gp.quicksum(d_iksta[i,k]*data_loader_a2.leavetime[i] for i in data_loader_a2.FD[t]))/60\n",
    "                                                          for k in data_loader_a2.C) for t in data_loader_a2.Dates ))\n",
    "\n",
    "# m.setObjectiveN(x_ikdh.sum(),index=1,priority=7)\n",
    "# m.setObjectiveN(gp.quicksum(x_ikfo[i,k] for i in data_loader_a2.F for k in data_loader_a2.C2), index=2, priority=6)\n",
    "\n",
    "# m.setObjective(a1-a2)\n",
    "\n",
    "\n",
    "#m.setObjectiveN(-z.sum(),index = 0,weight =0.8)\n",
    "#m.setObjectiveN(x_ikdh.sum(),index=1,weight=0.2)\n",
    "#m.setObjectiveN(gp.quicksum(x_ikfo[i,k] for i in data_loader_a.F for k in data_loader_a.C2),index=2,weight=0.05)\n",
    "\n",
    "\n",
    "M=10000\n",
    "#对X的约束\n",
    "m.addConstrs(x_ikfo[i,k]==0 for i in data_loader_a2.F for k in data_loader_a2.C1 )\n",
    "m.addConstrs(x_ikcap[i,k]==0 for i in data_loader_a2.F for k in data_loader_a2.C3 )\n",
    "m.addConstrs(x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k]<=1 for i in data_loader_a2.F for k in data_loader_a2.C)\n",
    "\n",
    "#对Z的约束\n",
    "m.addConstrs(gp.quicksum(x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k] for k in data_loader_a2.C)<=M*(z[i]) for i in data_loader_a2.F)\n",
    "m.addConstrs(x_ikcap.sum(i,'*')== z[i] for i in data_loader_a2.F )\n",
    "m.addConstrs(x_ikfo.sum(i,'*')== z[i] for i in data_loader_a2.F )\n",
    "m.addConstrs(M*z[j]>=gp.quicksum(x_ikcap[j,k]+x_ikfo[j,k]+x_ikdh[j,k] for k in data_loader_a2.C) for j in data_loader_a2.F)\n",
    "\n",
    "#对Y的约束\n",
    "m.addConstrs(y_ijk[i,j,k]==0 for i,j in data_loader_a2.FF for k in data_loader_a2.C)\n",
    "#m.addConstrs(gp.quicksum(y_ijk[i,j,k] for j in F)<=x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k] for i in F for k in C)\n",
    "#m.addConstrs(gp.quicksum(y_ijk[j,i,k] for j in F)<=x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k] for i in F for k in C)\n",
    "\n",
    "#对roaster周期的约束\n",
    "m.addConstrs(r_jkfin[j,k]== 0 for j in data_loader_a2.nonF_arrive_base[0] for k in data_loader_a2.C)\n",
    "# m.addConstrs(r_iksta[i,k]== 0 for i in non_leave_temp_base[k] for k in data_loader_a2.C)\n",
    "\n",
    "m.addConstrs(gp.quicksum(r_iksta[i,k] for i in data_loader_a2.F )<=1  for k in data_loader_a2.C)\n",
    "m.addConstrs(gp.quicksum(r_jkfin[j,k] for j in data_loader_a2.F )-gp.quicksum(r_iksta[i,k] for i in leave_temp_base_dic[k] ) == 0 for k in data_loader_a2.C)\n",
    "\n",
    "#第一问中航班对应周期的约束\n",
    "m.addConstrs(1-(y_ijk.sum(i,'*',k)+r_iksta[i,k]+r_jkfin[i,k])<=M*(1-(x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k])) for i in data_loader_a2.F for k in data_loader_a2.C)\n",
    "m.addConstrs(y_ijk.sum(i,'*',k)+r_jkfin[i,k]-(y_ijk.sum('*',i,k)+r_iksta[i,k]) == 0 for i in data_loader_a2.F for k in data_loader_a2.C)\n",
    "\n",
    "m.addConstrs(y_ijk.sum(i,'*',k)+r_jkfin[i,k] <= x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k]  for i in data_loader_a2.F for k in data_loader_a2.C)\n",
    "m.addConstrs(y_ijk.sum('*',i,k)+r_iksta[i,k] <= x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k]  for i in data_loader_a2.F for k in data_loader_a2.C)\n",
    "\n",
    "#第二问\n",
    "#对V的约束\n",
    "m.addConstrs(v_ijk[i,j,k]==0 for i,j in data_loader_a2.FF1 for k in data_loader_a2.C)\n",
    "m.addConstrs(v_ijk[i,j,k] <= y_ijk[i,j,k] for i in data_loader_a2.F for j in data_loader_a2.F for k in data_loader_a2.C)\n",
    "\n",
    "\n",
    "#对duty执勤的约束\n",
    "m.addConstrs(gp.quicksum(d_iksta[i,k] for i in data_loader_a2.FD[t]) - gp.quicksum(d_jkfin[i,k] for i in data_loader_a2.FD[t]) == 0 for i in data_loader_a2.F for k in data_loader_a2.C for t in data_loader_a2.Dates)\n",
    "m.addConstrs(gp.quicksum(d_iksta[i,k] for i in data_loader_a2.FD[t])   <= gp.quicksum(x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k] for i in data_loader_a2.FD[t]) for i in data_loader_a2.F for k in data_loader_a2.C for t in data_loader_a2.Dates)\n",
    "m.addConstrs(gp.quicksum(d_iksta[i,k] for i in data_loader_a2.FD[t])*M >= gp.quicksum(x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k] for i in data_loader_a2.FD[t]) for k in data_loader_a2.C for t in data_loader_a2.Dates)\n",
    "\n",
    "#m.addConstrs(d_iksta[i,k] for i in data_loader_a.FD[t]  <= x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k] for i in data_loader_a.FD[t] for k in data_loader_a.C for t in data_loader_a.Dates)\n",
    "\n",
    "#第二问中航班对应执勤，执勤对应周期的约束\n",
    "m.addConstrs((v_ijk.sum(i,'*',k)+ r_jkfin[i,k] ==  d_jkfin[i,k]) for i in data_loader_a2.F for k in data_loader_a2.C)\n",
    "m.addConstrs((v_ijk.sum('*',i,k)+ r_iksta[i,k] ==  d_iksta[i,k]) for i in data_loader_a2.F for k in data_loader_a2.C)\n",
    "\n",
    "#m.addConstrs((d_jkfin[i,k] <= x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k])  for i in data_loader_a.F for k in data_loader_a.C)\n",
    "#m.addConstrs((d_iksta[i,k] <= x_ikcap[i,k]+x_ikfo[i,k]+x_ikdh[i,k])  for i in data_loader_a.F for k in data_loader_a.C)\n",
    "\n",
    "#第二问中其余约束\n",
    "m.addConstrs(gp.quicksum(d_jkfin[i,k]*data_loader_a2.arrivetime[i] for i in data_loader_a2.FD[t]) - gp.quicksum(d_iksta[i,k]*data_loader_a2.leavetime[i] for i in data_loader_a2.FD[t]) <= 720 for k in data_loader_a2.C for t in data_loader_a2.Dates )\n",
    "m.addConstrs(gp.quicksum((x_ikcap[i,k]+x_ikfo[i,k])*(data_loader_a2.leavetime[i]-data_loader_a2.arrivetime[i]) for i in data_loader_a2.FD[t]) <= 600 for k in data_loader_a2.C for t in data_loader_a2.Dates )\n",
    "#m.addConstrs(gp.quicksum(d_jkfin[i,k]*data_loader_a.arrivetime[i] for i in data_loader_a.FD[t]) - gp.quicksum(d_iksta[i,k]*data_loader_a.leavetime[i] for i in data_loader_a.FD[t]) >= 0 for k in data_loader_a.C for t in data_loader_a.Dates )\n",
    "\n",
    "\n",
    "#m.addConstr(z.sum('*')==206)\n",
    "# m.addConstr(gp.quicksum(gp.quicksum(data_loader_a.DCost[k]*(gp.quicksum(d_jkfin[i,k]*data_loader_a.arrivetime[i] for i in data_loader_a.FD[t]) \n",
    "#                     - gp.quicksum(d_iksta[i,k]*data_loader_a.leavetime[i] for i in data_loader_a.FD[t]))\n",
    "#                 for k in data_loader_a.C) for t in data_loader_a.Dates )==[1324573-100000,1324573+100000])\n",
    "#对执勤时长平衡的约束（辅助目标）\n",
    "# m.addConstrs(a1>=(gp.quicksum(gp.quicksum(d_jkfin[i,k]*data_loader_a.arrivetime[i] for i in data_loader_a.FD[t]) for t in data_loader_a.Dates) - gp.quicksum(gp.quicksum(d_iksta[i,k]*data_loader_a.leavetime[i] for i in data_loader_a.FD[t]) for t in data_loader_a.Dates)) for k in data_loader_a.C)\n",
    "# m.addConstrs(a2<=(gp.quicksum(gp.quicksum(d_jkfin[i,k]*data_loader_a.arrivetime[i] for i in data_loader_a.FD[t]) for t in data_loader_a.Dates) - gp.quicksum(gp.quicksum(d_iksta[i,k]*data_loader_a.leavetime[i] for i in data_loader_a.FD[t]) for t in data_loader_a.Dates)) for k in data_loader_a.C)\n",
    "m.addConstr(z.sum()==98)\n",
    "# m.addConstr(gp.quicksum(gp.quicksum(data_loader_a2.DCost[k]*(gp.quicksum(d_jkfin[i,k]*data_loader_a2.arrivetime[i] for i in data_loader_a2.FD[t]) \n",
    "#                                                           - gp.quicksum(d_iksta[i,k]*data_loader_a2.leavetime[i] for i in data_loader_a2.FD[t]))/60\n",
    "#                                                           for k in data_loader_a2.C) for t in data_loader_a2.Dates) == 250163)\n",
    "# m.addConstr(x_ikdh.sum()==)\n",
    "# m.addConstr(gp.quicksum(x_ikfo[i,k] for i in data_loader_a1.F for k in data_loader_a1.C2)==0)\n",
    "             \n",
    "m.update()\n",
    "m.optimize()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a67bd71a",
   "metadata": {},
   "source": [
    "# 结果计算与导出"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "6e60d6b0",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "7\n",
      "0\n"
     ]
    }
   ],
   "source": [
    "count_dh = 0\n",
    "count_bench = 0\n",
    "for i in data_loader_a2.F:\n",
    "    for k in data_loader_a2.C:\n",
    "        if x_ikdh[i,k].x > 0.9:\n",
    "            count_dh += 1\n",
    "    for k in data_loader_a2.C2:\n",
    "        if x_ikfo[i,k].x>0.9:\n",
    "            count_bench += 1\n",
    "print(count_dh)\n",
    "print(count_bench)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0f035c44",
   "metadata": {},
   "outputs": [],
   "source": [
    "for i in data_loader_a2.F:\n",
    "    for k in data_loader_a2.C:\n",
    "        if x_ikcap[i,k].x > 0.9:\n",
    "            results_dic['em_no'].append(k)\n",
    "            for key, value in data_loader.F_dic.items():\n",
    "                if value == data_loader_a2.F_dic[i]:\n",
    "                    results_dic['fl_no'].append(key)\n",
    "                    break\n",
    "            results_dic['cls'].append(1)\n",
    "        if x_ikfo[i,k].x > 0.9:\n",
    "            results_dic['em_no'].append(k)\n",
    "            for key, value in data_loader.F_dic.items():\n",
    "                if value == data_loader_a2.F_dic[i]:\n",
    "                    results_dic['fl_no'].append(key)\n",
    "                    break\n",
    "            results_dic['cls'].append(2)\n",
    "        if x_ikdh[i,k].x > 0.9:\n",
    "            results_dic['em_no'].append(k)\n",
    "            for key, value in data_loader.F_dic.items():\n",
    "                if value == data_loader_a2.F_dic[i]:\n",
    "                    results_dic['fl_no'].append(key)\n",
    "                    break\n",
    "            results_dic['cls'].append(3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "38e16c71",
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8da1eca4",
   "metadata": {},
   "outputs": [],
   "source": [
    "df = pd.DataFrame(results_dic)\n",
    "df"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "acc61695",
   "metadata": {},
   "outputs": [],
   "source": [
    "from ResultViewer import RViewer"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8cb5565d",
   "metadata": {},
   "outputs": [],
   "source": [
    "rv = RViewer(data_loader, data_cls='a')\n",
    "rv.load_results_from_df(df)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "2de799bf",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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RvT0O/smsmnTptRa57obTFIpJYsWyYsBAAAA5ot5EfY99NBDuf3227Nx48a57gpTtGTJkuy7777Zcccd57orAAAAAAvevAj7br/99jzmMY/JXnvtlVLKXHeHSaq15uc//3luv/327L///nPdHQAAAIAFb87Dvk2bNmXjxo3Za6+9snjxnHeHKdprr71y9913Z9OmTU7pBQAAAJhjc57ODH1Hnxl926ehv5vvWgQAAACYe/NyKt2tS1fMyn6X3bVmVvYLAAAAAPPBnM/sm6/e85735JBDDsnBBx+cq6++esr3X7VqVe65556tlp922mm54447ZqCHAAAAALCleTmzb66tXbs2F198ca6//vrce++9OfDAA/PDH/5wSqcar1q1KgMDA3nc4x63xfLzzz9/ZjsLAAAAAF1m9o3iiiuuyCte8YosWbIke+65Z04//fR88pOfzPLly7NixYq86U1vSpKsXLkyRx99dI488si88IUvzLe+9a1861vfysDAQNauXZtXvvKVOf7447fY98DAQG677bbNt6+77roceuihOeSQQ3L66aen1ppVq1bljW98Y1760pfmGc94Rt797nf38vABAAAA2E4J+0bx4x//OHvvvffm26eddlruvPPOfPSjH83ll1+eK6+8MnfeeWeS5MlPfnKuvfbanHnmmfnzP//zPPOZz8zq1atz4IEH5qMf/Wg+/vGPj9lOrTUnnnhiPvKRj+RLX/pSfvKTn+Rf/uVfkiTXXnttLr300nz+85/P+973vtk9YAAAAACaIOwbxeMe97j88pe/3Hz75S9/eX75y1/mj//4j3PKKadk0aJFue+++5Iky5cvT5I873nPy6233jqldn72s59l0aJFecpTnpIkedGLXpS1a9cmSY477rjsscceefzjH58HH3xwBo4KAAAAgNYJ+0Zx1FFH5YorrsimTZvyox/9KDfddFPOOOOMXH755fnABz6QWuvmbW+44YYkyU033ZQDDjhg8/JddtklGzZsSJItth9u7733zqZNmzaf1vvFL34xBx54YJJkt912m4UjAwAAAKBlfqBjFEcffXRuuOGGHHbYYdm4cWMuuuiiXHrppTn44IOz1157ZY899siPfvSjJMlPf/rTHHXUUbnvvvvy4Q9/ePM+3vzmN+cNb3hDks4v+z7/+c/fqp1SSj7ykY/kNa95TWqtWb58eU444YRcfPHFvTlQAAAAAJpSxpp1Nhv6+/vr4ODgFsseeeSRfPe7380BBxyQHXbYoWd9mQkrV67Mfvvtl5NOOmmuuzJntue/HwAAAMD2ppSyrtbaP9Z6M/umYeXKlXPdBQAAAADYzHf2AQAAAEAjhH0AAAAA0IgFdRrvA2u/M+rynQ98eo970rZbl67Yatmyu9bMQU9m3mjHlrRzfPPBQqnxyONs7fjmktpOnlrNHrWdPLWaPWo7eWo1e9R28tRq9qjt1Iz3mb7lz/stMbMPAAAAABoxL2f2nXvzxlnZ72llVnYLAAAAAPOCmX2jWLlyZZYtW5aBgYHNl0ceeSRr167NueeeO619n3/++dO6/6ZNm3LsscdmYGAgr3zlKzcv/9rXvpbly5dnYGAgq1at2rz83HPPzdq1a6fVJgAAAADbh3k5s28+OOWUU/Knf/qnWyw78MADc+CBB05rv+eff35OO+20bb7/4OBg7rrrrlx33XVbLL/qqqvy+te/Pm94wxu2WH7GGWdsc1sAAAAAbF/M7JuC1atX56STTtpi2X777ZfLL788z3/+83PVVVclSb73ve9lxYoVedGLXpSXvOQluffee/NP//RPGRgYyB133JGBgYGcffbZ47b1iU98IsuXL8/y5cvzzne+M0ly3nnn5fd+7/fyzW9+MwMDA/nABz6QJHnxi1+ciy66KH//93+fgYGB3H777Zv3c9JJJ2X16tWbbw/d95BDDsnxxx+f++67L0ly5pln5rDDDsvy5ctzww03bD7eE088MW9605umHXICAAAAMPumNbOvlPI3SX4ryU+TvLbWeteM9GoeeO9737s5vLvkkkuy7777jrntZz7zmdxwww1ZvLhTziuvvDLLly/Pu971rnz2s5/NPffck1e/+tV59atfnf32229z+PbQQw/lxS9+8Rb72mOPPbJq1ar8yZ/8Sb72ta/lsY99bAYGBrJixYqcfvrpOf7447cK8D796U9n5cqV2W+//bYKI0d63etel/PPPz8vfOELc+GFF+amm27Khg0bcsMNN+SLX/xibrnllpx88smbA79PfOIT+fjHP54LL7xwihUEAAAAoNe2OewrpbwkySFJXpBkIMlfJzl5Zro190Y7jXcsf/u3f7s56EuSP/iDP8gZZ5yRo48+Or/+67+e8847b9T77bjjjluEdkNuvPHGLFu2LH19fUmSQw45JGvXrs0LX/jCqR/ICN/+9rc37+fUU09NrTXvete78oMf/CADAwNJkg0bNmze/uijj87RRx897XYBAAAAmH3TOY33mCSX1lo3JflcOsHfFkopp5dSBocuw0Okljz2sY/d4vY111yTU089NZ/5zGey884752Mf+9jmdZs2bUqS1FrH3N/++++f//zP/8wvfvGLPPLII7n++uvzm7/5mzPS16c//en5yle+kiT5y7/8y7z73e/Os5/97BxxxBFZvXp1rrzyyrzqVa8a89gAAAAAmL+mcxrvY5LcniS11lpK2W3kBrXW85JsntbW398/dsLVkGc+85k59dRTs2jRotRat/iRjDe84Q057LDD8uCDD+bGG28c9f59fX0577zzcswxxyRJXvGKV+Tggw+ekb596EMfypve9KYkyd57751LLrkku+22Wz7/+c/n8MMPz4YNG3Lyyc1M0AQAAABYUKYT9v0yyfCAb8dp9mWzMw5aMlO72sIDaye33cqVK0ddPjAwsPlU1yG33XbbVts997nPzZo1a0bdx1lnnZWzzjprwj4ce+yxOfbYY7daPvw7/4Ybq8+rVq3a4vZznvOcfP7zn99qu7/7u7/batloxwsAAADA/DWd03ivT3JkkpRSDkjS5jm6AAAAALCdmE7Yd0WSQ0sp5yf51yQXzEiPAAAAAIBtss2n8dZaHyilHJrkpUn+pdZ6/cx1CwAAAACYqul8Z19qrfcnuWyG+gIAAAAATMN0TuMFAAAAAOYRYR8AAAAANKLUWnvWWH9/fx0cHNxi2SOPPJLvfve7OeCAA7LDDjskSdafs35W2u87q29W9ruQjfb3AwAAAGB2lFLW1Vr7x1pvZt8oVq5cmWXLlmXFihU5/vjjMzKgnOq+Vq1aNeb6k046KatXr97m/Y80MDCQ2267bcb2BwAAAMD2Q9g3hlNOOSVr1qzJEUcckde85jVz3R0AAAAAmJCwbwKnnHJKvvzlL+eWW27JEUcckRUrVuR1r3tdaq2ptebUU0/NoYcemuXLl+dTn/pUkmTNmjX5L//lv+Soo47KmjVrJmzjc5/7XI488sg89alPzRe+8IUkycc+9rEccsghecELXpD3v//9SZK77747xxxzTAYGBrJixYrcfvvtSZILL7wwBx10UF72spflpz/96SxVAgAAAID5bnEvG3v4J3fl1qUrtli23x2re9mFKVu8eHH6+vpy3HHH5cMf/nAOP/zwHHfccfnUpz6V5cuX56lPfWr+4R/+If/2b/+WCy64IMccc0xOPfXUXHLJJXnuc5+bgYGBJMnZZ5+da6+9dot9v/3tb0+SfPvb385nP/vZXHbZZbn00kvz7Gc/O295y1vyzW9+M7vsskue/vSn54QTTsgPf/jD/NEf/VFe9rKX5c/+7M/y0Y9+NCeddFLe8Y535Dvf+U7uv//+LNvvN/Lgt27NA/c8kCTZ+cCn97Re24PhY3DZXROHsUzeyMe3+s6OkXUeot4zZ6HUeLTjbO0Y55oa995Ej98WH99ef2eP2k5ei4+t+cZrysQ8ZmfPyN9W8JsI81tPw77t0aZNm/KLX/wid9xxR972trclSe65554MDg7m0EMPzS233JLf/u3fTl9fX+67774kyW233ZYDDzwwSXLIIYckSd761rfmrW9961b7f//735/Xvva1KaXk8Y9/fB588MF8//vfz3333Zdjjz02SbLTTjtlcHAwO++8cy655JJcdNFFeeihh7LHHnvkBz/4QZ72tKdl1113za677poDfuM3Zr8oAAAAAMxLTuOdwAc/+MEceuihecELXpCLL744q1evztlnn51nPOMZueyyy1JrzdVXX53f/d3f3XyfJz/5yfnGN76RWmtuvPHGCdvYbbfdtri9bNmy7LvvvrnmmmuyevXqnHLKKdlzzz3z9re/PS9/+ctz5ZVX5pnPfGaSZN999833vve9PPDAA7nnnnvy/R/+cGYLAAAAAMB2Y17O7JsP00Hf+9735sorr0x/f3/+8R//MXfccUde97rX5YEHHsjuu++eiy++OPvss08uuOCCHHbYYXnKU56SH//4x6m15j3veU9OPPHE9PX15eGHH55y23vttVfOPPPMHHHEEXnwwQfzvOc9L/vss0+OO+64nHnmmfnQhz6UpUuXZs8998wTn/jEvPnNb87BBx+cJz7xidnrcY+b+WIAAAAAsF2Yl2HfXFu5cmVWrly5xbJ99tkn11xzzVbLbrnllq3uf+SRR+bmm2+eVFurVq3afH1gYGDzd/ydcMIJOeGEE7bY9rjjjstxxx231T5OP/30nH766UmSB9Z+Z1LtAgAAANAep/ECAAAAQCOEfQAAAADQiDkP+0opSZJa6xz3hG0x9Hcb+jsCAAAAMHfm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\n",
      "text/plain": [
       "<Figure size 1600x640 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "rv.draw_ef_gantt(save='results/q2_a.png')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "0f1374f7",
   "metadata": {},
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'rv' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mNameError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m~\\AppData\\Local\\Temp/ipykernel_12356/3573451114.py\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mdf_a\u001b[0m \u001b[1;33m=\u001b[0m \u001b[0mrv\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mget_results_df_a\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m\u001b[0;32m      2\u001b[0m \u001b[0mdf_a\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n",
      "\u001b[1;31mNameError\u001b[0m: name 'rv' is not defined"
     ]
    }
   ],
   "source": [
    "df_a = rv.get_results_df_a()\n",
    "df_a"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e487c04a",
   "metadata": {},
   "outputs": [],
   "source": [
    "df_a.to_csv('results/q2_a_UnconveredFlights.csv', index=0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c1001a94",
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "df_b = rv.get_results_df_b()\n",
    "df_b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0678ab37",
   "metadata": {},
   "outputs": [],
   "source": [
    "df_b.to_csv('results/q2_a_CrewRosters.csv', index=0)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python [conda env:mim] *",
   "language": "python",
   "name": "conda-env-mim-py"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
